<?php
// +----------------------------------------------------------------------------
// | PHP@妖孽 [三十年河东三十年河西,莫欺少年穷.!]
// +----------------------------------------------------------------------------
// | Copyright (c) 2015 http://www.itdaodan.com All rights reserved.
// +----------------------------------------------------------------------------
// | Author: PHP@妖孽 <dino_ma@163.com>
// +----------------------------------------------------------------------------
/**
 *@todo 微信扫码支付
 +------------------------------------------------------------------------------
 * @version   1.0
 * @since 2015-4-22
 +------------------------------------------------------------------------------
 */
namespace Think\Pay\Driver;
class Wechatpay extends  \Think\Pay\Pay{
    protected $gateway = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    protected $config  = array(
        'appid'  => '',
        'mch_id' => '',
        'key'    => '',
    	'notify_url'=>''
    );

    public function check() {
        if (!$this->config['appid'] || !$this->config['mch_id'] || !$this->config['key']) {
            E("微信扫码支付设置有误！");
        }
        return true;
    }

    /**
     * 支付通知验证
     * @param $notify string
     * @return array|bool
     */
    public function verifyNotify($notify) {
        $notify    = self::fromXml($notify);
        $checkSign = $this->generateSign($notify) == $notify['sign'];
        if ($checkSign && $notify['result_code'] == 'SUCCESS' && $notify['return_code'] == 'SUCCESS' && $notify['mch_id'] ==$this->config['mch_id']) {
        	$this->setInfo($notify);
            return array('time_paid' => strtotime($notify['time_end']), 'transaction_no' => $notify['transaction_id']);
        } else {
            return false;
        }
    }

    /**
     * 获取二维码链接
     * @param PayVo $vo
     * @return array
     */
    public function buildRequestForm(\Think\Pay\PayVo $vo) {
//     	echo (int)($vo->getFee()*100);die;
//     	echo str_replace(array('.',','), array('',''), number_format($vo->getFee(),2));die;
        $param = array(
            'appid'            => $this->config['appid'],
            'mch_id'           => $this->config['mch_id'],
            'nonce_str'        => substr(md5(time()), 0, 12),
            'body'             =>  $vo->getTitle(),
            'detail'           => $vo->getBody(),
            'out_trade_no'     => $vo->getOrderNo(),
            'total_fee'        => (int)($vo->getFee()*100),
            'spbill_create_ip' => get_client_ip(),
            'notify_url'       => $this->config['notify_url'],
            'trade_type'       => 'NATIVE',
            'product_id'       => $vo->getOrderNo(),
            'attach'		   =>json_encode($vo->getParam()),
        );

        $param['sign'] = $this->generateSign($param);

        $xml      = static::toXml($param);
        $response = $this->http($this->gateway, $xml);
        $result   = self::fromXml($response);

        if ($result['return_code'] == 'FAIL')
            E($result['return_msg']);

        if ($result['result_code'] == 'FAIL')
            E($result['err_code'] . ' ' . $result['err_code_des']);

        if ($result['sign'] == $this->generateSign($result)) {
            return array('code_url' => $result['code_url'], 'type' => 'qrcode');
        } else {
            E($response);
        }

    }


    /**
     * 生成签名
     * @param $param
     * @return string
     */
    private function generateSign($param) {
        ksort($param);
        reset($param);

        $arg = '';
        foreach ($param as $key => $value) {
            if ($value && $key != 'sign') {
                $arg .= "$key=$value&";
            }
        }
        return strtoupper(md5($arg . 'key=' . $this->config['key']));
    }

    /**
     * 数组转XML
     * @param $values
     * @return string
     * @throws \Think\Exception
     */
    private static function toXml($values) {
        if (!is_array($values) || count($values) <= 0) {
            E("数组数据异常！");
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * xml转数组
     * @param $xml
     * @return mixed
     */
    private static function fromXml($xml) {
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
    

   
	/**
	 * 以post方式提交xml到对应的接口url
	 */
	private static function http($url,$xml, $useCert = false, $second = 30){		
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		
		//如果有配置代理这里就设置代理
		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	
		if($useCert == true){
			//设置证书
			//使用证书：cert 与 key 分别属于两个.pem文件
// 			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
// 			curl_setopt($ch,CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH);
// 			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
// 			curl_setopt($ch,CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH);
		}
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		//运行curl
		$data = curl_exec($ch);
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else { 
			$error = curl_errno($ch);
			curl_close($ch);
		}
	}
	
	/**
	 * @todo 设置订单信息
	 * @param unknown $notify
	 */
	protected function setInfo($notify) {
		$info =  array();
		//支付状态
		$info['status'] = ($notify['return_code'] == 'SUCCESS' && $notify['result_code'] == 'SUCCESS') ? true : false;
		$info['money'] = $notify['total_fee'];
		$info['out_trade_no'] = $notify['out_trade_no'];
		$this->info = $info;
	}
	
	
	/**
	 * @todo 查询订单状态
	 * @param \Think\Pay\PayVo $vo
	 */
	public function getOrderQuery(\Think\Pay\PayVo $vo) {
	    $param = array(
	        'appid'            => $this->config['appid'],
	        'mch_id'           => $this->config['mch_id'],
	        'nonce_str'        => substr(md5(time()), 0, 12),
	        'out_trade_no'     => $vo->getOrderNo(),
	    );
	    $param['sign'] = $this->generateSign($param);
	    $xml      = static::toXml($param);
	    $response = $this->http('https://api.mch.weixin.qq.com/pay/orderquery', $xml);
	    $result   = self::fromXml($response);
	    if ($result['return_code'] == 'FAIL')
	        E($result['return_msg']);
	        if ($result['result_code'] == 'FAIL')
	            E($result['err_code'] . ' ' . $result['err_code_des']);
	            if ($result['sign'] == $this->generateSign($result)) {
	                return array(
	                    'out_trade_no' => $result['out_trade_no'], //支付网关订单号
	                    'transaction_id' => $result['transaction_id'],//交易号
	                    'attach'=>$result['attach'],//附加参数
	                    'money'=>$result['cash_fee']/100,//交易金额
	                );
	            } else {
	                E($response);
	            }
	    
	}
}